import { md5 } from 'js-md5'

export function uuidGenerator(): string {
  return ([1e7] as any + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c: any) =>
    (
      c
      ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
    ).toString(16))
}

export function sleep(ms?: number) {
  return new Promise(resolve => setTimeout(resolve, ms || 5000))
}

export function retry<T>(
  request: (...args: any[]) => Promise<T>,
  maxRetries: number = 3,
  retryDelay: number = 1000,
): (...args: any[]) => Promise<T> {
  return async (...args: any[]) => {
    let retries = 0

    while (retries <= maxRetries) {
      try {
        const result = await request(...args)
        return result
      }
      catch (error) {
        if (retries === maxRetries) {
          throw error
        }
        retries++
        await new Promise(resolve => setTimeout(resolve, retryDelay))
      }
    }
    throw new Error('未知错误')
  }
}

export function asyncOnce(cb: (...args: any[]) => Promise<any>) {
  const map: Record<string, any> = {}

  return (...args: any[]) => {
    const key = JSON.stringify(args)

    return (map[key] ??= new Promise((resolve, reject) => {
      cb(...args)
        .then(resolve)
        .catch(reject)
        .finally(() => {
          // map[key] = null
        })
    }))
  }
}

interface IGenerateSignParams {
  requestMethod: string
  requestParams: Record<string, any>
  secret: string
  token: string
  version: string
  timestamp: number
}

export function generateSign({
  requestMethod,
  requestParams,
  secret,
  token,
  version,
  timestamp,
}: IGenerateSignParams) {
  const requestParamsStr = Object.keys(requestParams || {})
    .filter(key => requestParams[key] !== undefined)
    .reduce((str, v) => {
      str += v
      return str
    }, '')
  const str = `${token}${requestParamsStr}${requestMethod.toUpperCase()}${timestamp}${version}${secret}`
  const sign = md5(str)

  return sign
}
